package code

import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import io.netty.handler.codec.CorruptedFrameException
import io.netty.handler.codec.LengthFieldBasedFrameDecoder
import io.netty.handler.codec.TooLongFrameException
import io.netty.handler.codec.serialization.ObjectDecoder
import kotlin.experimental.and

/**
 *
 * [LengthFieldBasedFrameDecoder]
 * 在此基础上面 读取头长度修改为  可变长度  默认4字节
 * * @author pc
 *
 */
class MyLengthFieldBasedFrameDecoder
/**
 * Creates a new instance.
 *
 * @param maxFrameLength
 * the maximum length of the frame.  If the length of the frame is
 * greater than this value, [TooLongFrameException] will be
 * thrown.
 * @param lengthFieldOffset
 * the offset of the length field
 * @param initialBytesToStrip
 * the number of first bytes to strip out from the decoded frame
 * @param failFast
 * If <tt>true</tt>, a [TooLongFrameException] is thrown as
 * soon as the decoder notices the length of the frame will exceed
 * <tt>maxFrameLength</tt> regardless of whether the entire frame
 * has been read.  If <tt>false</tt>, a [TooLongFrameException]
 * is thrown after the entire frame that exceeds <tt>maxFrameLength</tt>
 * has been read.
 */
@JvmOverloads constructor(private val maxFrameLength: Int, private val lengthFieldOffset: Int,
                          /**跳过头长度字节 */
                          private val initialBytesToStrip: Int = 0, private val failFast: Boolean = true) : ByteToMessageDecoder() {
    private var discardingTooLongFrame: Boolean = false
    private val lengthIncludesLengthFieldLength: Boolean = false
    private var tooLongFrameLength: Long = 0
    private var bytesToDiscard: Long = 0

    init {

        if (maxFrameLength <= 0) {
            throw IllegalArgumentException(
                    "maxFrameLength must be a positive integer: " + maxFrameLength)
        }

        if (lengthFieldOffset < 0) {
            throw IllegalArgumentException(
                    "lengthFieldOffset must be a non-negative integer: " + lengthFieldOffset)
        }

        if (initialBytesToStrip < 0) {
            throw IllegalArgumentException(
                    "initialBytesToStrip must be a non-negative integer: " + initialBytesToStrip)
        }

        if (lengthFieldOffset > maxFrameLength) {
            throw IllegalArgumentException(
                    "maxFrameLength (" + maxFrameLength + ") " +
                            "must be equal to or greater than " +
                            "lengthFieldOffset (" + lengthFieldOffset + ")")
        }
    }

    @Throws(Exception::class)
    override fun decode(ctx: ChannelHandlerContext, `in`: ByteBuf, out: MutableList<Any>) {
        val decoded = decode(ctx, `in`)
        if (decoded != null) {
            out.add(decoded)
        }
    }

    /**
     * Create a frame out of the [ByteBuf] and return it.
     *
     * @param   ctx             the [ChannelHandlerContext] which this [ByteToMessageDecoder] belongs to
     * @param   in              the [ByteBuf] from which to read data
     * @return  frame           the [ByteBuf] which represent the frame or `null` if no frame could
     * be created.
     */
    @Throws(Exception::class)
    protected fun decode(ctx: ChannelHandlerContext, `in`: ByteBuf): Any? {
        if (discardingTooLongFrame) {
            var bytesToDiscard = this.bytesToDiscard
            val localBytesToDiscard = Math.min(bytesToDiscard, `in`.readableBytes().toLong()).toInt()
            `in`.skipBytes(localBytesToDiscard)
            bytesToDiscard -= localBytesToDiscard.toLong()
            this.bytesToDiscard = bytesToDiscard

            failIfNecessary(false)
        }
        var actualLengthFieldOffset = `in`.readerIndex() + lengthFieldOffset
        var shift = 0
        var frameLength = 0
        var headLenth = 0
        while (shift < 32) {
            if (`in`.readableBytes() < 1)
                return null
            val b = `in`.getByte(actualLengthFieldOffset++)
            frameLength = frameLength or ((b and 0x7F).toLong() shl shift).toInt()
            headLenth++
            if (b and 0x80.toByte() === 0.toByte()) {
                break
            }
            shift += 7
        }

        if (!lengthIncludesLengthFieldLength) {
            frameLength += headLenth
        }

        if (frameLength < 0) {
            //            in.skipBytes(lengthFieldEndOffset);
            throw CorruptedFrameException(
                    "negative pre-adjustment length field: " + frameLength)
        }

        if (frameLength > maxFrameLength) {
            val discard = (frameLength - `in`.readableBytes()).toLong()
            tooLongFrameLength = frameLength.toLong()

            if (discard < 0) {
                // buffer contains more bytes then the frameLength so we can discard all now
                `in`.skipBytes(frameLength)
            } else {
                // Enter the discard mode and discard everything received so far.
                discardingTooLongFrame = true
                bytesToDiscard = discard
                `in`.skipBytes(`in`.readableBytes())
            }
            failIfNecessary(true)
            return null
        }

        // never overflows because it's less than maxFrameLength
        val frameLengthInt = frameLength
        if (`in`.readableBytes() < frameLengthInt) {
            return null
        }

        if (initialBytesToStrip > frameLengthInt) {
            `in`.skipBytes(frameLengthInt)
            throw CorruptedFrameException(
                    "Adjusted frame length (" + frameLength + ") is less " +
                            "than initialBytesToStrip: " + initialBytesToStrip)
        }
        `in`.skipBytes(initialBytesToStrip)

        // extract frame
        val readerIndex = `in`.readerIndex()
        val actualFrameLength = frameLengthInt - initialBytesToStrip
        val frame = extractFrame(ctx, `in`, readerIndex, actualFrameLength)
        `in`.readerIndex(readerIndex + actualFrameLength)
        return frame
    }

    private fun failIfNecessary(firstDetectionOfTooLongFrame: Boolean) {
        if (bytesToDiscard == 0L) {
            // Reset to the initial state and tell the handlers that
            // the frame was too large.
            val tooLongFrameLength = this.tooLongFrameLength
            this.tooLongFrameLength = 0
            discardingTooLongFrame = false
            if (!failFast || failFast && firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength)
            }
        } else {
            // Keep discarding and notify handlers if necessary.
            if (failFast && firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength)
            }
        }
    }

    /**
     * Extract the sub-region of the specified buffer.
     *
     *
     * If you are sure that the frame and its content are not accessed after
     * the current [.decode]
     * call returns, you can even avoid memory copy by returning the sliced
     * sub-region (i.e. <tt>return buffer.slice(index, length)</tt>).
     * It's often useful when you convert the extracted frame into an object.
     * Refer to the source code of [ObjectDecoder] to see how this method
     * is overridden to avoid memory copy.
     */
    protected fun extractFrame(ctx: ChannelHandlerContext, buffer: ByteBuf, index: Int, length: Int): ByteBuf {
        return buffer.slice(index, length).retain()
    }

    private fun fail(frameLength: Long) {
        if (frameLength > 0) {
            throw TooLongFrameException(
                    "Adjusted frame length exceeds " + maxFrameLength +
                            ": " + frameLength + " - discarded")
        } else {
            throw TooLongFrameException(
                    "Adjusted frame length exceeds " + maxFrameLength +
                            " - discarding")
        }
    }
}
/**
 * Creates a new instance.
 *
 * @param maxFrameLength
 * the maximum length of the frame.  If the length of the frame is
 * greater than this value, [TooLongFrameException] will be
 * thrown.
 * @param lengthFieldOffset
 * the offset of the length field
 */
/**
 * Creates a new instance.
 *
 * @param maxFrameLength
 * the maximum length of the frame.  If the length of the frame is
 * greater than this value, [TooLongFrameException] will be
 * thrown.
 * @param lengthFieldOffset
 * the offset of the length field
 * @param initialBytesToStrip
 * the number of first bytes to strip out from the decoded frame
 */
